﻿/*----------------------------------------------------------------
//  <copyright file="SmsHostedService.cs" company="MicroCloud@151504200868">
//      Copyright © 2020-2024 MicroCloud Corporation, All rights reserved.
//  </copyright>
//  <site>https://gitee.com/chenmm123/microclouds</site>
//  <last-editor>cmm</last-editor>
//  <last-date>2023-12-06 11:32</last-date>
//----------------------------------------------------------------*/

namespace MicroCloud.SMS
{
    #region "短信托管服务"
    /// <summary>
    /// 短信托管服务
    /// </summary>
    /// <typeparam name="TVerifyCode">验证码短信记录实体类型</typeparam>
    /// <typeparam name="TVerifyCodeKey">验证码短信记录实体主键类型</typeparam>
    /// <typeparam name="IVerifyCodeEventData">短信验证码发送事件数据类型</typeparam>
    public class SmsHostedService<TVerifyCode, TVerifyCodeKey, IVerifyCodeEventData> : IHostedService, IDisposable
        where TVerifyCode : VerifyCodeBase<TVerifyCodeKey>
        where TVerifyCodeKey : IEquatable<TVerifyCodeKey>
        where IVerifyCodeEventData : SmsVerifyCodeEventDataBase<TVerifyCode, TVerifyCodeKey>, new()
    {
        #region "属性"
        #region "获取 服务提供者对象"
        /// <summary>
        /// 获取 服务提供者对象
        /// </summary>
        protected IServiceProvider ServiceProvider { get; }
        #endregion
        #region "获取 日志对象"
        /// <summary>
        /// 获取 日志对象
        /// </summary>
        protected ILogger Logger { get; }
        #endregion

        #region "获取 过期移除定时器"
        /// <summary>
        /// 获取 过期移除定时器
        /// </summary>
        protected Timer ExpiredRemoveTimer { get; }
        #endregion

        #endregion

        #region "构造函数"
        #region "初始化一个短信托管服务的新实例"
        /// <summary>
        /// 初始化一个短信托管服务的新实例
        /// </summary>
        /// <param name="provider">服务提供者</param>
        public SmsHostedService(IServiceProvider provider)
        {
            ServiceProvider = provider;
            Logger = provider.GetLogger(GetType());

            ExpiredRemoveTimer = new Timer(ExecuteExpiredRemove, null, Timeout.Infinite, 0);
        }
        #endregion

        #endregion

        #region "方法"
        #region "异步启动"
        /// <summary>
        /// 异步启动
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task StartAsync(CancellationToken cancellationToken)
        {
            Logger.LogInformation("启动短信托管服务");

            SetExpiredRemoveTask();

            return Task.CompletedTask;
        }
        #endregion
        #region "异步停止"
        /// <summary>
        /// 异步停止
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task StopAsync(CancellationToken cancellationToken)
        {
            Logger.LogInformation("停止短信托管服务");

            ExpiredRemoveTimer?.Change(Timeout.Infinite, 0);

            return Task.CompletedTask;
        }
        #endregion
        #region "释放资源"
        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                ExpiredRemoveTimer?.Dispose();
            }
        }
        #endregion

        #endregion

        #region "受保护的方法"
        #region "设置过期移除任务"
        /// <summary>
        /// 设置过期移除任务
        /// </summary>
        protected void SetExpiredRemoveTask()
        {
            DateTime now = DateTime.Now;
            DateTime zeroOClock = DateTime.Today.AddHours(0.0); //凌晨00:00:00
            if (now > zeroOClock)
            {
                zeroOClock = zeroOClock.AddDays(1.0);
            }
            int msUntilFour = (int)((zeroOClock - now).TotalMilliseconds);

            ExpiredRemoveTimer.Change(msUntilFour, Timeout.Infinite);
        }
        #endregion
        #region "执行过期移除"
        /// <summary>
        /// 执行过期移除
        /// </summary>
        /// <param name="state"></param>
        protected void ExecuteExpiredRemove(object state = null)
        {
            try
            {
                using var scope = ServiceProvider.CreateScope();
                IServiceProvider scopedProvider = scope.ServiceProvider;
                var options = scopedProvider.GetService<IConfiguration>()?.GetSmsOptions();
                if (options?.StorageDays > 0)
                {
                    var storageDays = options.StorageDays;
                    var nowDay = DateTime.Now.Date;
                    var expiredDay = nowDay.AddDays(storageDays);
                    #region "验证码短信移除逻辑"
                    var verifyCodeRepository = scopedProvider.GetService<IRepository<TVerifyCode, TVerifyCodeKey>>();
                    var entities = verifyCodeRepository.QueryAsNoTracking(o => o.CreatedTime.Date < expiredDay).ToArray();
                    if (entities?.Length > 0)
                    {
                        IVerifyCodeEventData eventData = new()
                        {
                            OperationType = SmsOperationType.Delete,
                            Entities = entities,
                        };
                        var result = verifyCodeRepository.Delete(entities);
                        if (result.Succeeded)
                        {
                            var eventBus = scopedProvider.GetService<IEventBus>();
                            eventBus?.Publish(this, eventData);
                        }
                    }
                    Logger.LogDebug("日期 {expiredDay} 以前共计 {length} 条验证码短信记录移除成功", expiredDay, entities?.Length ?? 0);
                    #endregion
                }
                else
                {
                    Logger.LogDebug("未设定短信存储天数，跳过短信过期记录移除执行逻辑。");
                }
            }
            catch (System.Exception ex)
            {
                Logger.LogError(ex, "{msg}", ex.Message);
            }
            finally
            {
                //再次设定
                SetExpiredRemoveTask();
            }
        }
        #endregion

        #endregion

    }
    #endregion

}
